import os
import subprocess

import loguru
from log_config import logger

from cmake import CmakeDepends
import re
from ruamel import yaml
import mantiDB
from yaml_tool import read_yaml_file
from yaml_tool import write_yaml_file

def find_file(library_name, prefix, suffix, search_paths):

    """
    查找指定库的文件，具有特定的前后缀。

    参数：
    - library_name (str): 需要查找的库名称。
    - prefix (str): 文件的前缀，例如 "Find"。
    - suffix (str): 文件的后缀，例如 ".cmake"。
    - search_paths (list): 需要搜索的基路径列表。

    返回：
    - str: 找到的文件的完整路径，如果未找到则返回 None。
    """
    # 遍历每个基路径
    for base_path in search_paths:
        # 使用 os.walk 遍历目录树
        for root, dirs, files in os.walk(base_path):
            for file in files:
                # 检查文件名是否匹配指定的前缀、库名称和后缀
                if file.lower() == f'{prefix}{library_name}{suffix}'.lower():
                    # 找到匹配的文件，返回完整路径
                    return os.path.join(root, file)
    # 如果未找到任何匹配的文件，返回 None
    return None

PRE_ANALYSE_MAP_PATH = 'pre_analyse_map.yaml'

# 返回 CmakeDepends
def ProcessConfig(config_path, working_dir):
    cmake = CmakeDepends()
    if config_path:
        cmake.parse(config_path)
        directory, filename = os.path.split(config_path)
        VAR_MAP = {'QT_MAJOR_VERSION': ['5', '6']}
        logger.info(f"Processing File {config_path} ......")
        # 中间解析结果存放的路径：
        mid_depends_save_path = os.path.join(working_dir, 'cmake_mid_depends')
        cmake.save_only_depends(mid_depends_save_path, VAR_MAP, f"{filename}-mid-depends.yaml")
    return cmake

def ProcessVersion(version_path):
    # 根据VersionConfig.cmake提取版本号，无用
    try:
        with open(version_path, 'r', encoding='utf-8') as file:
            for line in file:
                if 'set(PACKAGE_VERSION' in line:
                    # 使用正则表达式匹配版本号
                    match = re.search(r'set\(PACKAGE_VERSION\s+(.*)\)', line)
                    if match:
                        version = match.group(1).strip("'\" ")
                        return version
    except Exception:
        # print(f"文件 '{version_path}' 不存在或无法访问")
        pass
    return 'unknown'  # 如果未找到版本信息失效

class CmakeOrderProcessor:
    def __init__(self,working_dir, os_version, config_cmake_path = "/usr/lib64/cmake",module_cmake_path = "/usr/share/cmake/Modules"):
        # Module 模式
        self.working_dir = working_dir
        self.module_prefix = 'Find'
        self.module_suffix = '.cmake'
        # Config 模式
        self.config_prefix1 = ''
        self.config_suffix1 = 'Config.cmake'
        self.config_prefix2 = ''
        self.config_suffix2 = '-config.cmake'
        # self.version_suffix = 'ConfigVersion.cmake'
        self.find_package_search_paths = [
            config_cmake_path,  # Config 模式下，欧拉系统中的路径
            module_cmake_path,  # Module 模式下，欧拉系统中的路径
            working_dir,   # Some projects have extra cmake files
            'package-mapping-test\\cmake',  # Config 模式，在WIN中测试用
            'package-mapping-test\\Modules',  # Module 模式，在WIN中测试用
            'package-mapping-test/cmake',
            'package-mapping-test/Modules'
        ]
        # 访问数组，用来加速
        self.visited = set()
        self.result = {}
        self.os_version = os_version
        # mantiDB.save_data(self.os_version)




    # 返回: 依赖信息, 版本号
    def ProcessFiles(self, lib_name, config_path):
        # 处理VersionConfig.cmake
        # version = ProcessVersion(version_path)
        # 处理Config.cmake
        # 先查表，如果表里没有，再递归解析
        map = read_yaml_file('filtered_map.yaml')
        cmake_file_name1 = f'Find{lib_name}.cmake'
        cmake_file_name2 = f'{lib_name}Config.cmake'

        find_orders = set()
        if cmake_file_name1 in map.keys():
            find_orders.update(map.get(cmake_file_name1))
        if cmake_file_name2 in map.keys():
            find_orders.update(map.get(cmake_file_name2))
        if len(find_orders) != 0:
            map_result = {"buildRequires": list(find_orders)}
            return map_result

        cmake = ProcessConfig(config_path, self.working_dir)
        return cmake.libraries_dict

    # find_package、find_path、pkg_check_modules、find_library、target_link_libraries。
    def process_find_package(self, pkg_name):
        # 处理 find_package 命令
        # 需要递归，生成新的依赖
        # 混合模式，先按照Config模式找，找不到，再按照Module模式找
        config_path = find_file(pkg_name, self.config_prefix1, self.config_suffix1, self.find_package_search_paths)
        if config_path is None:
            config_path = find_file(pkg_name, self.config_prefix2, self.config_suffix2, self.find_package_search_paths)
        if config_path is None:
            config_path = find_file(pkg_name, self.module_prefix, self.module_suffix, self.find_package_search_paths)

        # version_path = find_file(pkg_name, self.version_suffix, self.find_package_search_paths)
        value = {}
        value["config_path"] = config_path
        # value["version_path"] = version_path
        value["depends_info"]= self.ProcessFiles(pkg_name, config_path)
        # 返回含有新依赖的字典（DEBUG用）：
        # value = {
        #     'config_path': config_path,
        #     'version_path': version_path,
        #     'depends_info': depends_info,
        #     'version_info': version_info
        # }
        # find_package.DtkCore无果，故在结果中加入pkgconfig(DtkCore)
        add_rpms = set()
        pkg_check_rpms, _ = self.process_pkg_check_modules(pkg_name)
        config_cmake_rpms, _ = self.process_find_cmake(pkg_name)
        tgt_link_rpms1, _ = self.process_target_link_libraries(pkg_name)
        tgt_link_rpms2, _ = self.process_target_link_libraries(f"{pkg_name.replace(':','')}Core")
        special_rpms = self.process_special_rpms(pkg_name)

        add_rpms.update(pkg_check_rpms)
        add_rpms.update(config_cmake_rpms)
        add_rpms.update(tgt_link_rpms1)
        add_rpms.update(tgt_link_rpms2)
        add_rpms.update(special_rpms)
        return (value,add_rpms), True



    def process_find_path(self, file_name):
        # 处理 find_path 命令
        # 因为file_name可能会是 pulse/pulse.h,选择保留原始字符串，不再截断
        # name = os.path.basename(file_name).partition(".")[0]
        name = file_name.partition(".")[0]
        full_order = f"find_path.{name}"
        if full_order not in self.visited:
            self.visited.add(full_order)
            rpms = mantiDB.search(name,".h",self.os_version)
            self.result[name] = rpms
        else:
            rpms = self.result[name]

        # 返回.h对应的rpm数组,无需递归
        # loguru.logger.info(f"find_path->{name}.h")
        # loguru.logger.info(f"get rpms:{rpms}")
        return rpms, False



    def process_pkg_check_modules(self,file_name):
        # 处理 pkg_check_modules 命令
        # pkg_check_modules.xxx  -> Just map to pkgconfig(xxx)
        name = os.path.basename(file_name)
        full_order = f"pkg_check_modules.{name}"
        if full_order not in self.visited:
            self.visited.add(full_order)
            rpms = mantiDB.search(name, ".pc",self.os_version)
            self.result[name] = rpms
        else:
            rpms = self.result[name]
        # 返回.pc对应的rpm数组,无需递归
        # loguru.logger.info(f"pkg_check_modules->{name}.pc or pkgconfig({name})")
        # loguru.logger.info(f"get rpms:{rpms}")
        # rpms.add(f"pkgconfig({name})")
        return rpms, False


    def process_find_library(self, lib_name):
        # 处理 find_library 命令

        full_order = f"pkg_find_library.{lib_name}"
        if full_order not in self.visited:
            self.visited.add(full_order)

            rpms = mantiDB.search(lib_name, ".so",self.os_version)
            self.result[lib_name] = rpms
        else:
            rpms = self.result[lib_name]
        # 返回.a或.so对应的rpm数组,无需递归
        # loguru.logger.info(f"find_library->(lib){lib_name}.so or (lib){lib_name}.a")
        # loguru.logger.info(f"get rpms:{rpms}")
        return rpms, False

    def process_target_link_libraries(self, file_name):
        # 处理 target_link_libraries 命令
        # 去除冒号： Qt5::Core => Qt5Core 从而去找 libQt5Core.so
        is_in = False
        if '::' in file_name:
            is_in = True
            left = file_name.partition("::")[0]
            right = file_name.partition("::")[2]
            file_name = file_name.replace(":", "")
        # print(file_name)
        name = os.path.basename(file_name).partition(".")[0]
        full_order = f"pkg_target_link_libraries.{name}"
        if full_order not in self.visited:
            self.visited.add(full_order)
            rpms = mantiDB.search(name, ".so",self.os_version)
            if is_in:
                pkgconfig_name = f"pkgconfig({left}-{right})"
                rpms.update(mantiDB.search(pkgconfig_name,"",self.os_version))
                pkgconfig_name = f"pkgconfig({left}{right})"
                rpms.update(mantiDB.search(pkgconfig_name, "", self.os_version))
                pkgconfig_name = f"pkgconfig({left})"
                rpms.update(mantiDB.search(pkgconfig_name, "", self.os_version))
                pkgconfig_name = f"pkgconfig({right})"
                rpms.update(mantiDB.search(pkgconfig_name, "", self.os_version))
            self.result[name] = rpms
        else:
            rpms = self.result[name]
        # 返回.a或.so对应的rpm数组,无需递归
        # loguru.logger.info(f"target_link_libraries->(lib){file_name}.so or (lib){file_name}.a")
        # loguru.logger.info(f"get rpms:{rpms}")
        return rpms, False

    def process_find_program(self, file_name):
        # 处理 find_program 命令
        name = os.path.basename(file_name).partition(".")[0]
        full_order = f"find_program.{name}"

        if full_order not in self.visited:
            self.visited.add(full_order)
            rpms = mantiDB.search(name, "program",self.os_version)
            # if name == "DGraph":
            #     print(f"find:{rpms}")
            self.result[name] = rpms
        else:
            rpms = self.result[name]
        # rpms.add(f"pkgconfig({name})")
        # loguru.logger.info(f"process_find_program->{name}")
        # loguru.logger.info(f"get rpms:{rpms}")
        return rpms, False

    def process_find_cmake(self, file_name):
        # 处理 find_package 命令时调用
        name = os.path.basename(file_name).partition(".")[0]
        name = name.replace(':', "")
        full_order = f"find_package.{name}"

        if full_order not in self.visited:
            self.visited.add(full_order)
            rpms = mantiDB.search(name, ".cmake",self.os_version)
            # if name == "DGraph":
            #     print(f"find:{rpms}")
            self.result[name] = rpms
        else:
            rpms = self.result[name]
        # loguru.logger.info(f"process_find_program->{name}")
        # loguru.logger.info(f"get rpms:{rpms}")
        return rpms, False

    def process_special_rpms(self, pkg_name):
        special_rpms = set()
        if pkg_name == 'ECM':
            special_rpms.add('extra-cmake-modules')
        if pkg_name == 'MPI_Fortran' or pkg_name == 'F77' or pkg_name == 'F90' or pkg_name == 'F2003':
            special_rpms.add('gcc-gfortran')
        # if 'KF5' in pkg_name or 'kf5' in pkg_name:
        #     special_rpms.add('kf5-rpm-macros')

        return special_rpms

    def process(self, order):
        order_name, _, pkg_or_file_name = order.partition('.')
        # 得去除夹杂的版本信息："ibus-1.0 >= 1.5.0" -> "ibus-1.0"
        pkg_or_file_name = pkg_or_file_name.split('>')[0].split('<')[0].split('=')[0]
        # 删除多余的引号和空格
        pkg_or_file_name = pkg_or_file_name.replace('"', '').replace("'", "").replace(" ","")
        # print(pkg_or_file_name)
        rpms = []
        add_rpms = set()
        need_recursion = False
        if 'find_package' in order_name:
            return self.process_find_package(pkg_or_file_name)
        elif 'find_path' in order_name or 'find_file' in order_name or 'check_include_file' in order_name:
            add_rpms, need_recursion = self.process_find_path(pkg_or_file_name)
        elif 'pkg_check_modules' in order_name or 'pkg_search_module' in order_name:
            add_rpms, need_recursion = self.process_pkg_check_modules(pkg_or_file_name)
        elif 'find_library' in order_name:
            add_rpms, need_recursion = self.process_find_library(pkg_or_file_name)
        elif 'target_link_libraries' in order_name:
            add_rpms, need_recursion = self.process_target_link_libraries(pkg_or_file_name)
        elif 'find_program' in order_name:
            add_rpms, need_recursion = self.process_find_program(pkg_or_file_name)
        else:
            logger.warning(f'cannot match any order. order:{order}, order_name:{order_name}, pkg_or_file_name:{pkg_or_file_name}')
            pass

        if len(add_rpms) == 0:
            logger.debug(f'add_rpms for cmake is empty, order:{order}, order_name:{order_name}, pkg_or_file_name:{pkg_or_file_name}')

        rpms += list(add_rpms)
        return rpms, need_recursion

    def clear_result(self):
        self.visited.clear()
        self.result.clear()


if __name__ == '__main__':
    cmake = CmakeOrderProcessor('wuhu',"22.03-LTS-SP4")
    res = cmake.process_find_package('gsettings-desktop-schemas')
    print(res)